Google Play Billing

Google Play 结算系统  |  Google Play's billing system  |  Android Developers

Google 支付接入流程

一、注册开发者账号

注册链接https://play.google.com/apps/publish/signup/

1.1、注册 Google 账号并登陆

1.2、支付注册费用 ( $25 )

注册开发者账号是需要支付 $25 美元,需要使用国外信用卡或者是在国内申请的 visa 卡 (visa 信用卡建议申请双币卡 (就是在国内外都能使用的))。

1.2、填写账号详细信息

另外,如果要发布付费应用,还需要设置 Google Checkout 商家账户,也就是绑定一张非大陆的银行卡进行收款,建议是香港或者国外的。

二、新建一个 APP 项目

控制台链接https://play.google.com/apps/publish/?authuser=0

2.1、新建一个项目

2mlj4

2.2、配置应用版本

8eup2应用版本分四大类:

2.3、填写其他信息

在应用详情左侧的菜单栏中有对号标识的都必须填写

2.4、配置应用内商品

todo 待补充

商品类型说明:

三、接入 Google 支付

官方文档链接https://developer.android.com/google/play/billing/billing_overview?hl=zh-cn

准备好以上的工作之后就可以在 APP 中接入 Google 支付了。

google play 支付服务,具体使用可参考官方 demo

依赖包

 com.android.billingclient:billing:2.0.1

APP 中接入流程参考:

3.1、判断当前设备是否支持 Google 服务

添加 com.google.android.gms:play-sevices-base:17.0.0

	 /**
     * 检查 Google Play 服务
     */
    @JvmStatic
    fun onCheckGooglePlayServices(activity: Activity) {
        // 验证是否已在此设备上安装并启用Google Play服务,以及此设备上安装的旧版本是否为此客户端所需的版本
        val code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(activity)
        if (code == ConnectionResult.SUCCESS) {
            // 支持Google服务
            LogUtils.i(TAG, "支持Google服务")
        } else {
            GoogleApiAvailability.getInstance()
                    .makeGooglePlayServicesAvailable(activity)
                    .addOnCanceledListener {
                        LogUtils.w(TAG, "Google服务cancel")
                    }
                    .addOnFailureListener { e->
                        LogUtils.printStackTrace(e)
                        LogUtils.w(TAG, "Google服务failure ${e.message}")
                    }
                    .addOnSuccessListener {
                        LogUtils.i(TAG, "Google服务success")
                    }
                    .addOnCompleteListener {
                        LogUtils.i(TAG, "Google服务complete")
                    }
            ToastAndDialog.makeText(activity, "不支持Google服务.").show()
            //不支持时,可以利用getErrorDialog得到一个提示框, 其中第2个参数传入错误信息
            //提示框将根据错误信息,生成不同的样式
            //例如,我自己测试时,第一次Google Play Service不是最新的,
            //对话框就会显示这些信息,并提供下载更新的按键
            if (GoogleApiAvailability.getInstance().isUserResolvableError(code)) {
                GoogleApiAvailability.getInstance().getErrorDialog(activity, code, 0).show()
            }
        }
    }

3.2、连接 Google play 服务

这步可能会失败,有可能是你的 Google 账号的付款方式设置的不对,详见 4.1 测试账号准备

 public BillingClientManager init(Context context) {
        mBillingClient = BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build();
        return this;
    }
 /**
     * 连接支付服务
     */
    public void connectToPlayBillingService(BillingClientListener listener) {
        if (mBillingClient.isReady() == false) {
            mBillingClient.startConnection(this);
        }
    }
boolean isConnected =mBillingClient.isReady()

3.3、加载商品列表

 mBillingClient.querySkuDetailsAsync(getInappSkuDetailsParams(), responseListener);

查询参数params:

 //在应用内商品页面中配置的商品的商品ID
    private static final String SUB_VIP_YEAR = "sub_vip_year";
    private static final String SUB_VIP_MOUTH = "sub_vip_mouth";
    private static final String SUB_THREE_MOUTH_VIP = "sub_three_mouth_vip";

    private static final String PERMANENT_FREE_VIP = "vip_of_one_yearly";


    public static List<String> INAPP_SKUS = Arrays.asList(PERMANENT_FREE_VIP);
    public static List<String> SUBS_SKUS = Arrays.asList(SUB_VIP_YEAR, SUB_VIP_MOUTH);

 public static SkuDetailsParams getInappSkuDetailsParams() {
        String skuType = BillingClient.SkuType.INAPP;
        return SkuDetailsParams.newBuilder().setSkusList(INAPP_SKUS).setType(skuType).build();
    }

    public static SkuDetailsParams getSubsProductList() {

        String skuTypeSubs = BillingClient.SkuType.SUBS;
        return SkuDetailsParams.newBuilder().setSkusList(SUBS_SKUS).setType(skuTypeSubs).build();

    }

3.3、购买商品

这步可能会失败,有可能是你的 Google 账号的付款方式设置的不对,详见 4.1 测试账号准备

 public void launchBillingFlow(Activity activity, SkuDetails skuDetails) {
        if (mBillingClient == null) {
            return;
        }
        if (BillingClient.SkuType.INAPP.equals(skuDetails.getType())) {
            BillingFlowParams purchaseParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
            mBillingClient.launchBillingFlow(activity, purchaseParams);
            return;
        }
        BillingFlowParams.Builder builder = BillingFlowParams.newBuilder();
        String productId = "";
        if (localPurchaseSet.size() != 0) {
            for (Purchase purchase : localPurchaseSet) {
                String sku = purchase.getSku();
                if (ProductRepository.getSubsProductList().getSkusList().contains(sku)) {
                    productId = sku;
                }
            }
        }
        //如果之前有订阅过 并且不同于将要订购的产品 那么就将它进行升级 例如月会员升级到年会员
        if (StringUtils.isNotBlank(productId) && !productId.equals(skuDetails.getSku())) {
            builder.setOldSku(productId);
        }
        BillingFlowParams params = builder.setSkuDetails(skuDetails).build();
        mBillingClient.launchBillingFlow(activity, params);
    }
 @Override
    public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
        if (billingResult == null) {
            return;
        }
        LogUtils.d(TAG, "Purchases发生了变化=>" + billingResult.getResponseCode());

        switch (billingResult.getResponseCode()) {
            case BillingClient.BillingResponseCode.OK:
                acknowledged(purchases);
                break;
            case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
                if (mBillingClient.isReady() == false) {
                    mBillingClient.startConnection(this);
                }
                break;
            case BillingClient.BillingResponseCode.USER_CANCELED:
                //用户取消了支付
                if (getBillingClientListener() != null) {
                    getBillingClientListener().onConsumeFail(AppUtils.getString(R.string.user_canceled_play));
                }
                break;
            default:
                if (getBillingClientListener() != null) {
                    getBillingClientListener().onConsumeFail(billingResult.getDebugMessage());
                }
                break;
        }


    }
     /**
     * 确认消费订单
     */
    private void acknowledged(@Nullable List<Purchase> purchases) {
        Set<Purchase> inapPpurchaseSet = new HashSet<>();
        Set<Purchase> subsPurchases = new HashSet<>();
        //未确认的账单调用确认方法
        for (Purchase purchase : purchases) {
            if (ProductRepository.SUBS_SKUS.contains(purchase.getSku())) {
                subsPurchases.add(purchase);
            } else if (ProductRepository.INAPP_SKUS.contains(purchase.getSku())) {
                inapPpurchaseSet.add(purchase);
            }
        }
        handleConsumablePurchasesAsync(inapPpurchaseSet, null);
        acknowledgeNonConsumablePurchasesAsync(subsPurchases, null);
    }

四、如何测试购买

官方文档链接https://developer.android.com/google/play/billing/billing_testing?hl=zh-cn

4.1、测试账号准备

在连接服务或付款时,会去 Google 商店中验证你当前在商店中登录的账号是否有购买商品的资格 (中国区的 Google 账号没有设置付款方式时会连接不上)。

如何使你的 Google 账号能够在应用内购买商品?

在设置付款详情时,需要注意的是,你的账单地址需要填写外国地址。你可以打开 Google map 随便点击一个地址,上面会显示相应的地址和邮编等信息,填写上去就 OK 了。

4.2、在测试人员列表中添加需要测试的测试账号

发布到内部测试渠道的 APP,如果其他人需要测试购买流程,需要在测试人员列表中添加测试人的 Google 账号。

xetat

五、其他

5.1、购买成功之后能获取到什么信息?

参数名 参数解释
orderId 订单 ID
packageName 应用名
productId 商品 ID
purchaseTime 购买时间
purchaseToken 购买令牌

5.2、能获取到的订单信息的方式有哪些?

查询订单分两种方式查询:

5.3、订阅商品需要注意哪些?

官方文档https://developer.android.com/google/play/billing/billing_subscriptions?hl=zh-cn

用户订购的商品会自动续订,用户可以在 Google play 商店中查看并操作 (取消订阅等操作) 自己订阅的商品。到期时用户续订是否成功,在客户端去查询订单信息的时候是不能确定的,需要通过后端接入 Google Play Developer API 才能知晓用户是否已经续订成功。